/**
 * @author See Contributors.txt for code contributors and overview of BadgerDB.
 *
 * @section LICENSE
 * Copyright (c) 2012 Database Group, Computer Sciences Department, University of Wisconsin-Madison.
 */

#include <memory>
#include <iostream>
#include "buffer.h"
#include "exceptions/buffer_exceeded_exception.h"
#include "exceptions/page_not_pinned_exception.h"
#include "exceptions/page_pinned_exception.h"
#include "exceptions/bad_buffer_exception.h"
#include "exceptions/hash_not_found_exception.h"

namespace badgerdb {

    BufMgr::BufMgr(std::uint32_t bufs)
            : numBufs(bufs) {
        bufDescTable = new BufDesc[bufs];

        for (FrameId i = 0; i < bufs; i++) {
            bufDescTable[i].frameNo = i;
            bufDescTable[i].valid = false;
        }

        bufPool = new Page[bufs];

        int htsize = ((((int) (bufs * 1.2)) * 2) / 2) + 1;
        hashTable = new BufHashTbl(htsize);  // allocate the buffer hash table

        clockHand = bufs - 1;
        numBufs = bufs;
    }


    BufMgr::~BufMgr() {
        // BufMgr类的析构函数
        // 将缓冲池中所有脏页写回磁盘
        for (FrameId i = 0; i < numBufs; i++) {
            if (bufDescTable[i].dirty) {
                bufDescTable[i].file->writePage(bufPool[i]);
            }
        }
        //释放缓冲池
        delete[]bufPool;
        //释放BufDesc表
        delete[]bufDescTable;
        //释放哈希表
        delete hashTable;
    }

    void BufMgr::advanceClock() {
        //顺时针旋转时钟算法中的表针，将其指向缓冲池中下一个页框。
        clockHand = (clockHand + 1) % numBufs;
    }

    void BufMgr::allocBuf(FrameId &frame) {
        //使用时钟算法分配一个空闲页框
        int pinCnt_total = 0;
        for (int i = 0; i < numBufs; i++) {
            if (bufDescTable[i].pinCnt > 0){
                pinCnt_total ++;
            }
        }
        //如果缓冲池中所有页框都被固定了(pinned)，则抛出BufferExceededException异常。
        if (pinCnt_total == numBufs){
            throw BufferExceededException();
        }
        bool find = false;
        for (int i = 0; i < 2*numBufs; i++) {
            advanceClock();
            if (!bufDescTable[clockHand].valid) {
                find = true;
                frame = clockHand;
                break;
            }
            //页面有效
            if (!bufDescTable[clockHand].refbit && bufDescTable[clockHand].pinCnt == 0) {
                //分配的页框的编号通过参数frame返回
                //被分配的页框中包含一个有效页面，必须将该页面从哈希表中删除
                hashTable->remove(bufDescTable[clockHand].file, bufDescTable[clockHand].pageNo);
                find = true;
                frame = clockHand;
                break;
            } else{
                bufDescTable[clockHand].refbit = false;
            }
        }
        if (!find){
            throw BufferExceededException();
        }
        //如果页框中的页面是脏的，则需要将脏页先写回磁盘
        if (bufDescTable[clockHand].dirty) {
            bufDescTable[clockHand].file->writePage(bufPool[clockHand]);
        }
    }


    void BufMgr::readPage(File *file, const PageId pageNo, Page *&page) {
        try {
            FrameId fameNo = -1;
            hashTable->lookup(file, pageNo, fameNo);
            if (fameNo == -1) {
                std::cout << "不应该出现这种情况！\n";
                throw HashNotFoundException(file->filename(), pageNo);
            }
            //页面在缓冲池中
            bufDescTable[fameNo].refbit = true;
            bufDescTable[fameNo].pinCnt += 1;
            page = &bufPool[fameNo];
            return;
        } catch (HashNotFoundException e) {
            //页面不在缓冲池中
            Page new_page = file->readPage(pageNo);
            FrameId fameNo = -1;
            allocBuf(fameNo);
            bufPool[fameNo] = new_page;
            hashTable->insert(file, pageNo, fameNo);
            bufDescTable[fameNo].Set(file, pageNo);
            //将该页面插入到哈希表中
            page = &bufPool[fameNo];
            return;
        }
    }


    void BufMgr::unPinPage(File *file, const PageId pageNo, const bool dirty) {
        FrameId frameNo = -1;
        try {
            hashTable->lookup(file, pageNo, frameNo);
            if (bufDescTable[frameNo].pinCnt == 0) {
                throw PageNotPinnedException(file->filename(), pageNo, frameNo);
            }
            bufDescTable[frameNo].pinCnt -= 1;
            if (dirty) {
                bufDescTable[frameNo].dirty = true;
            }
        } catch (HashNotFoundException e) {
            return;
        }

    }

    void BufMgr::flushFile(const File *file) {
        for (FrameId i = 0; i < numBufs; i++) {
            BufDesc * bufDesc = &(bufDescTable[i]);
            if (bufDesc->file == file) {
                if (!bufDesc->valid) {
                    //如果检索到文件file的某个无效页，则抛出BadBufferException异常
                    throw BadBufferException(bufDesc->frameNo, bufDesc->dirty, bufDesc->valid, bufDesc->refbit);
                }
                if (bufDesc->pinCnt >= 1) {
                    //如果文件file的某些页面被固定住(pinned)，则抛出PagePinnedException异常。
                    throw PagePinnedException(file->filename(), bufDesc->pageNo, bufDesc->frameNo);
                }
                //如果页面是脏的，则调用file->writePage()将页面写回磁盘，并将dirty位置为false；
                if (bufDesc->dirty) {
                    bufDesc->file->writePage(bufPool[bufDesc->frameNo]);
                    bufDesc->dirty = false;
                }
                //将页面从哈希表中删除；
                hashTable->remove(file, bufDesc->pageNo);
                //调用BufDesc类的Clear()方法将页框的状态进行重置。
                bufDesc->Clear();
            }
        }
    }

    void BufMgr::allocPage(File *file, PageId &pageNo, Page *&page) {
        // file->allocatePage()返回这个新分配的页面
        Page new_page = file->allocatePage();
        FrameId frameNo = -1;
        allocBuf(frameNo);
        pageNo = new_page.page_number();
        //接下来，在哈希表中插入一条项目
        hashTable->insert(file, pageNo, frameNo);
        //调用Set()方法正确设置页框的状态
        bufDescTable[frameNo].Set(file, pageNo);
        bufPool[frameNo] = new_page;
        page = &bufPool[frameNo];
    }

    void BufMgr::disposePage(File *file, const PageId PageNo) {
        //如果该页面在缓冲池中，应该要将该页面所在的页框清空并从哈希表中删除该页面
        FrameId fameNo = -1;
        try {
            hashTable->lookup(file, PageNo, fameNo);
            bufDescTable[fameNo].Clear();
            hashTable->remove(file, PageNo);
        } catch (HashNotFoundException e) {
            //如果该页面不在缓冲池中
        }
        file->deletePage(PageNo);
    }

    void BufMgr::printSelf(void) {
        BufDesc *tmpbuf;
        int validFrames = 0;

        for (std::uint32_t i = 0; i < numBufs; i++) {
            tmpbuf = &(bufDescTable[i]);
            std::cout << "FrameNo:" << i << " ";
            tmpbuf->Print();

            if (tmpbuf->valid)
                validFrames++;
        }

        std::cout << "Total Number of Valid Frames:" << validFrames << "\n";
    }

}
